
import sys
from PyQt5.QtCore import QObject, pyqtSlot
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from ui.Ui_serial_widget import Ui_SerialWidget
from common.qt_worker import Worker
from common.utils import *
from drivers.driver_serial import scan_serial_ports, SerialDevice
from ui.Ui_main_window import Ui_MainWindow
from views.serial_setting_dialog import SerialSettingDialog


class SerialWidget(QWidget):

    def __init__(self, parent = None):
        super().__init__(parent)

        self.main_window: QMainWindow = parent
        self.ui = Ui_SerialWidget()
        self.ui.setupUi(self)

        self.init_ui()

        self.serial_ports = []

        # 开启子线程循环接收数据
        self.cur_serial_device: SerialDevice = None
        self.refresh_worker: Worker = None
        self.recv_worker: Worker = None

        self.run_refresh_worker()

    def run_refresh_worker(self):
        if self.refresh_worker is not None and self.refresh_worker.is_running:
            return

        self.refresh_worker = Worker(scan_serial_ports)
        self.refresh_worker.signal_connect(result_handler=self.on_refresh_serial_port_result)
        self.refresh_worker.start()

    def init_ui(self):
        self.ui.toolButton.clicked.connect(self.on_tool_button)
        self.ui.btn_open_device.clicked.connect(self.on_btn_open_device)
        self.ui.btn_serial_refresh.clicked.connect(self.run_refresh_worker)
        self.ui.btn_recv_clear.clicked.connect(self.ui.edit_recv.clear)
        self.ui.btn_send.clicked.connect(self.on_btn_send)

    def on_btn_send(self):
        text = self.ui.edit_send.toPlainText()
        if text == "":
            print("发送数据不能为空")
            return

        if self.cur_serial_device is None or not self.cur_serial_device.is_open():
            print("请先打开串口")
            return
        
        # 如果勾选了加换行符，则在发送数据后加上换行符
        if self.ui.cb_send_newline.isChecked():
            text += "\r\n"

        # 向串口发送数据
        self.cur_serial_device.write(f"{text}".encode("utf-8"))

    @pyqtSlot(bytes)
    def on_serial_data_received(self, data: bytes):
        print("on_serial_data_signal: ", decode_data(data))
        if self.ui.cb_recv_hex.isChecked():
            data_hex = decode_to_hex_str(data)
            self.ui.edit_recv.appendPlainText(data_hex)
        else:
            self.ui.edit_recv.appendPlainText(decode_data(data))

    def receive_data(self, worker: Worker):
        while worker.is_running:
            if self.cur_serial_device is None:
                break

            if not self.cur_serial_device.is_open():
                break

            data = self.cur_serial_device.readline()
            if data:
                worker.emit_msg(data)
            else:
                print("读取超时，或已断开！")
                break

    def on_btn_open_device(self):
        if self.cur_serial_device is not None:
            self.finish_serial()
            return

        if self.ui.cb_devices.count() == 0:
            print("No serial port available.")
            return

        serial_port = self.serial_ports[self.ui.cb_devices.currentIndex()]
        baud_rate = int(self.ui.cb_baudrate.currentText())

        self.open_serial_device(serial_port, baud_rate)

        # 开启WorkerThread
        self.recv_worker = Worker(self.receive_data).signal_connect(
            msg_handler=self.on_serial_data_received,
            finished_handler=self.finish_serial,
            error_handler=self.on_serial_error
        )
        self.recv_worker.start()

        self.update_ui()
    def on_serial_error(self, e):
        print("Serial error: ", e)

    def open_serial_device(self, serial_port, baud_rate):
        port, desc = serial_port
        serial_port = SerialDevice(port, baud_rate)
        success, msg = serial_port.open()
        if success:
            self.cur_serial_device = serial_port
            print(msg)
            self.main_window.statusBar().showMessage(msg)
        else:
            tip_msg = "请检查设备是否连接，或端口被其他软件占用。\r\n" + msg
            QMessageBox.warning(self.main_window, "打开设备失败", tip_msg)
            self.main_window.statusBar().showMessage(tip_msg)

    def finish_serial(self):
        if self.cur_serial_device is not None:
            self.cur_serial_device.close()
            self.cur_serial_device = None
            msg = f"Serial port closed！"
            self.main_window.statusBar().showMessage(msg)

        if self.recv_worker is not None:
            self.recv_worker.stop()
            self.recv_worker = None

        self.update_ui()

    def update_ui(self):
        """
        根据连接状态更新UI
        :return:
        """
        if self.cur_serial_device is not None:
            # 修改图标
            self.ui.label_connect_state.setPixmap(QPixmap(":/icon/conn"))
            # 修改按钮文字
            self.ui.btn_open_device.setText("关闭设备")
        else:
            # 修改图标
            self.ui.label_connect_state.setPixmap(QPixmap(":/icon/disc"))
            # 修改按钮文字
            self.ui.btn_open_device.setText("打开设备")

    def on_tool_button(self):
        # 创建一个自定义对话框，包含串口的所有相关设置
        dialog = SerialSettingDialog()
        # 显示对话框
        rst = dialog.exec_()
        print("dialog rst: ", rst)
        if rst == QDialog.Accepted:
            print("accepted")

    @pyqtSlot(object)
    def on_refresh_serial_port_result(self, ports):
        self.serial_ports = ports
        self.ui.cb_devices.clear()
        for port in self.serial_ports:
            # print(type(port)) # ListPortInfo
            # print(port.device, port.description, port.hwid, port.vid, port.pid, port.serial_number, port.location)
            device, description = port
            self.ui.cb_devices.addItem(description)

        self.main_window.statusBar().showMessage(f"刷新完毕，{len(self.serial_ports)} serial ports found.")


def on_refresh_serial_port_result(ports):
    for port in ports:
        print(port)


if __name__ == '__main__':
    app = QApplication(sys.argv)

    thread = Worker(scan_serial_ports)
    thread.signal_connect(result_handler=on_refresh_serial_port_result)
    thread.start()

    sys.exit(app.exec_())
